

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Mindstorm.Gesture;
using Mindstorm.Gesture.Config;

using Tuio;

public class TangibleInput : MonoBehaviour
{
	public float scanTimespan = 5f;
	public float scanDelay = 1f;
	public float angleSensibility = 3f;
	public float moveSensibility = 0.05f;
	public float stationaryDelay = 1f;
	
	public bool debugMode = false;
	public bool menuMode = false;
	
	private int layerMaskGamePlane;
	private int layerMaskClickable;
	private int layerMaskUI;
	private ObjectType prefabType;
	
	Dictionary<int, float> touchStartTimes = new Dictionary<int, float>();
	Dictionary<int, List<double>> touchAreas = new Dictionary<int, List<double>>();
	Dictionary<int, List<double>> touchPerimeters = new Dictionary<int, List<double>>();
	Dictionary<int, List<double>> touchRotations = new Dictionary<int, List<double>>();
	Dictionary<int, List<double>> touchRotationsNorm = new Dictionary<int, List<double>>();
	Dictionary<int, float> touchStartMoveTimes = new Dictionary<int, float>();
	Dictionary<int, GPObject> touchStartMoveObjects = new Dictionary<int, GPObject>();
	
	Dictionary<int, GameObject> touchScanLoader = new Dictionary<int, GameObject>();
	
	void Awake(){
		layerMaskGamePlane = 1 << LayerMask.NameToLayer("GamePlane");
		layerMaskClickable = 1 << LayerMask.NameToLayer("Clickable");
		layerMaskUI = 1 << LayerMask.NameToLayer("UI");

	}
	
	void Update()
	{
		UnityTouchFull[] allTouches = InputFullProxy.touches;
		
		foreach (UnityTouchFull t in allTouches)
		{
			switch (t.Touch.phase)
			{
			case TouchPhase.Began:
				addTouch(t);
				break;
			case TouchPhase.Ended:
				if(!menuMode){
					removeTouch(t);
				}
				break;
			case TouchPhase.Moved:
				if(!menuMode){
					updateTouch(t);
				}
				break;
			case TouchPhase.Stationary:
				break;
			default:
				break;
			}
		}
	}
	
	void addTouch(UnityTouchFull t){
		RaycastHit hit;
		Ray ray = Camera.main.ScreenPointToRay(t.Touch.position);
		
		if(!menuMode){
			touchStartTimes.Add(t.Touch.fingerId, Time.time);
			touchAreas.Add(t.Touch.fingerId,new List<double>());
			touchPerimeters.Add(t.Touch.fingerId,new List<double>());
			touchRotations.Add(t.Touch.fingerId,new List<double>());
			touchRotationsNorm.Add(t.Touch.fingerId,new List<double>());
			touchStartMoveTimes.Add(t.Touch.fingerId,0);
			touchStartMoveObjects.Add(t.Touch.fingerId,null);
			touchScanLoader.Add(t.Touch.fingerId,null);
			
			if(Physics.Raycast(ray.origin,ray.direction,out hit,Mathf.Infinity, layerMaskClickable)){
				hit.collider.transform.gameObject.GetComponent<Clickable>().onClick();
				touchStartTimes[t.Touch.fingerId] = 0;
				return;
			}
		}
			
		if(UICamera.currentCamera == null) return;
		
		ray = UICamera.currentCamera.ScreenPointToRay(t.Touch.position);
		if(Physics.Raycast(ray.origin,ray.direction,out hit,Mathf.Infinity, layerMaskUI)){
			UIButton btn = hit.collider.GetComponent<UIButton>();
			btn.SendMessage("OnPress",true);
			btn.SendMessage("OnClick");
			StartCoroutine(releaseButton(btn));
		}
		
		
	}
	
	IEnumerator releaseButton(UIButton btn){
		yield return new WaitForSeconds(0.5f);
		if(btn.gameObject.activeSelf){
			btn.SendMessage("OnPress",false);
		}
	}
	
	void addTangible(UnityTouchFull t)
	{
		prefabType = selectCorrectObjectType(t.Touch.fingerId,t.Touch.position.x);
		if(prefabType == ObjectType.Unknown){
			//click
			LevelHandler.Instance.tangibles.Add(t.Touch.fingerId, null);
			return;
		}
		
		RaycastHit hit;
		Ray ray = Camera.main.ScreenPointToRay(t.Touch.position);
		if(Physics.Raycast(ray.origin,ray.direction,out hit,Mathf.Infinity, layerMaskGamePlane)){
			
			float angle = (float)(t.Properties.Angle);
			if(angle <= 12 || angle >= 79){
				angle = 0;
			}else if(angle > 12 && angle <=34){
				angle = 22.5f;
			}else if(angle > 34 && angle <=56){
				angle = 45;
			}else{
				angle = 67.5f;
			}
			
			GameObject tangible = LevelHandler.Instance.createObject(new GPObject(t.Touch.fingerId, prefabType, hit.point.x, hit.point.y , angle ,Color.white));
			if(tangible != null){
				LevelHandler.Instance.nbUserObjects++;
			}
			
			if(tangible != null && prefabType != ObjectType.Filter){
				THistory.Instance.addAction(new TAddAction(new GPObject(tangible)));
			}
		}
		
	}
	
	ObjectType selectCorrectObjectType(int fingerId, double posX){
		double moyAreas = touchAreas[fingerId].Average();
		double moyPerims = touchPerimeters[fingerId].Average();
		
		
		if(debugMode){
			Debug.Log("moyArea = "+touchAreas[fingerId].Average());
			Debug.Log("moyPeri = "+touchPerimeters[fingerId].Average());
		}
		
		
		/*
		//17041630
		if(posX < 500){
			//left
			if(moyAreas > 700 && moyAreas < 2700 && moyPerims > 240 && moyPerims < 360){
				return ObjectType.Filter;
			}else if(moyAreas > 1900 && moyAreas < 3600 && moyPerims > 360 && moyPerims < 460){
				return ObjectType.Mixer;
			}else if(moyAreas > 3600 && moyAreas < 4900 && moyPerims > 390 && moyPerims < 480){
				return ObjectType.Mirror;
			}else if(moyAreas > 5000 && moyAreas < 7000 && moyPerims > 480 && moyPerims < 600){
				return ObjectType.Divider;
			}else{
				Debug.Log("Aucun tangible ne correspond a cette aire");
				return ObjectType.Unknown;
			}
		}else{
			//right
			if(moyAreas > 700 && moyAreas < 2700 && moyPerims > 240 && moyPerims < 390){
				return ObjectType.Filter;
			}else if(moyAreas > 2200 && moyAreas < 3700 && moyPerims > 385 && moyPerims < 480){
				return ObjectType.Mixer;
			}else if(moyAreas > 3700 && moyAreas < 5200 && moyPerims > 400 && moyPerims < 490){
				return ObjectType.Mirror;
			}else if(moyAreas > 5300 && moyAreas < 7000 && moyPerims > 480 && moyPerims < 600){
				return ObjectType.Divider;
			}else{
				Debug.Log("Aucun tangible ne correspond a cette aire");
				return ObjectType.Unknown;
			}
		}
		*/
		
		if(posX < 500){
			//left
			if(moyAreas > 700 && moyAreas < 2700 && moyPerims > 240 && moyPerims < 360){
				return ObjectType.Filter;
			}else if(moyAreas > 1900 && moyAreas < 3600 && moyPerims > 360 && moyPerims < 460){
				return ObjectType.Mirror;
			}else if(moyAreas > 3600 && moyAreas < 4600 && moyPerims > 390 && moyPerims < 480){
				return ObjectType.Mirror;
			}else if(moyAreas > 4600 && moyAreas < 7000 && moyPerims > 480 && moyPerims < 600){
				return ObjectType.Divider;
			}else{
				Debug.Log("Aucun tangible ne correspond a cette aire");
				return ObjectType.Unknown;
			}
		}else{
			//right
			if(moyAreas > 700 && moyAreas < 2700 && moyPerims > 240 && moyPerims < 390){
				return ObjectType.Filter;
			}else if(moyAreas > 2200 && moyAreas < 3700 && moyPerims > 385 && moyPerims < 480){
				return ObjectType.Mirror;
			}else if(moyAreas > 3700 && moyAreas < 4800 && moyPerims > 400 && moyPerims < 490){
				return ObjectType.Mirror;
			}else if(moyAreas > 4800 && moyAreas < 7000 && moyPerims > 480 && moyPerims < 600){
				return ObjectType.Divider;
			}else{
				Debug.Log("Aucun tangible ne correspond a cette aire");
				return ObjectType.Unknown;
			}
		}
	}
	
	void removeTouch(UnityTouchFull t)
	{
		if (touchStartTimes.ContainsKey(t.Touch.fingerId)){
			touchStartTimes.Remove(t.Touch.fingerId);
			touchAreas.Remove(t.Touch.fingerId);
			touchPerimeters.Remove(t.Touch.fingerId);
			touchRotations.Remove(t.Touch.fingerId);
			touchRotationsNorm.Remove(t.Touch.fingerId);
			touchStartMoveTimes.Remove(t.Touch.fingerId);
			touchStartMoveObjects.Remove(t.Touch.fingerId);
			
			if (LevelHandler.Instance.tangibles.ContainsKey(t.Touch.fingerId)){
				removeTangible(t);
			}
			
			if(touchScanLoader.ContainsKey(t.Touch.fingerId)){
				if(touchScanLoader[t.Touch.fingerId] != null){
					Destroy(touchScanLoader[t.Touch.fingerId]);
				}
				touchScanLoader.Remove(t.Touch.fingerId);
			}
			
		}
	}
	
	void removeTangible(UnityTouchFull t)
	{
		GameObject tangible = LevelHandler.Instance.tangibles[t.Touch.fingerId];
		LevelHandler.Instance.tangibles.Remove(t.Touch.fingerId);
		
		
		if(tangible != null){
			LevelHandler.Instance.nbUserObjects--;
			
			GPObject gpObj = new GPObject(tangible);
			
			if(!(gpObj.Type == ObjectType.Filter && gpObj.ObjColor == Color.white)){
				THistory.Instance.addAction(new TRemoveAction(gpObj));
			}
			Destroy(tangible);
		}
	}
	
	void updateTouch(UnityTouchFull t)
	{
		if (LevelHandler.Instance.tangibles.ContainsKey(t.Touch.fingerId)){
			updateTangible(t);	
		}else{
			
			RaycastHit hit;
			Ray ray = Camera.main.ScreenPointToRay(t.Touch.position);
			
			if (!touchStartTimes.ContainsKey(t.Touch.fingerId)) return;
			if (touchStartTimes[t.Touch.fingerId] == 0) return;
			
			if(Time.time - touchStartTimes[t.Touch.fingerId] > scanTimespan){
				if (LevelHandler.Instance.tangibles.ContainsKey(t.Touch.fingerId)) return;
				addTangible(t);
				if(touchScanLoader[t.Touch.fingerId] != null){
					Destroy(touchScanLoader[t.Touch.fingerId]);
					touchScanLoader.Remove(t.Touch.fingerId);
				}
				touchStartTimes[t.Touch.fingerId] = 0;
			}else{
				if(Time.time - touchStartTimes[t.Touch.fingerId] > scanDelay/2){
					if(touchScanLoader[t.Touch.fingerId] == null){
						touchScanLoader[t.Touch.fingerId] = Instantiate(Resources.Load("Prefabs/ScanLoader")) as GameObject;
					}
					if(Physics.Raycast(ray.origin,ray.direction,out hit,Mathf.Infinity, layerMaskGamePlane)){
						touchScanLoader[t.Touch.fingerId].transform.position = new Vector3(hit.point.x,hit.point.y,0);
						float area = (float)t.Properties.Area/3500;
						if(area<1){
							area = 1;	
						}
						touchScanLoader[t.Touch.fingerId].transform.localScale = new Vector3(area,area,1);
					}
					
				}
				
				if(Time.time - touchStartTimes[t.Touch.fingerId] > scanDelay){
					touchAreas[t.Touch.fingerId].Add(t.Properties.Area);
					touchPerimeters[t.Touch.fingerId].Add((t.Properties.Width+t.Properties.Height)*2);
				}
			}
			
			if(Physics.Raycast(ray.origin,ray.direction,out hit,Mathf.Infinity, layerMaskClickable)){
				hit.collider.transform.gameObject.GetComponent<Clickable>().onClick();
				touchStartTimes[t.Touch.fingerId] = 0;
			}
			
			
			
		}
		
	}
	
	void updateTangible(UnityTouchFull t)
	{
		if (!LevelHandler.Instance.tangibles.ContainsKey(t.Touch.fingerId)) return;
		if (LevelHandler.Instance.tangibles[t.Touch.fingerId] == null) return;
		GameObject tangible = LevelHandler.Instance.tangibles[t.Touch.fingerId];
		
		RaycastHit hit;
		Ray ray = Camera.main.ScreenPointToRay(t.Touch.position);
		if(Physics.Raycast(ray.origin,ray.direction,out hit,Mathf.Infinity, layerMaskGamePlane)){
			//position
			if(Vector2.Distance(new Vector2(hit.point.x,hit.point.y),new Vector2(tangible.transform.position.x,tangible.transform.position.y)) > moveSensibility ){
				if(touchStartMoveObjects[t.Touch.fingerId] == null){
					touchStartMoveObjects[t.Touch.fingerId] = new GPObject(tangible);
				}

				touchStartMoveTimes[t.Touch.fingerId] = Time.time;
				
				hit.point = new Vector3(hit.point.x,hit.point.y,0);
				tangible.transform.position = hit.point;
				
			}else{
				if(touchStartMoveTimes[t.Touch.fingerId] != 0 && Time.time - touchStartMoveTimes[t.Touch.fingerId] > stationaryDelay){
					if(!(touchStartMoveObjects[t.Touch.fingerId].Type == ObjectType.Filter && touchStartMoveObjects[t.Touch.fingerId].ObjColor == Color.white)){
						THistory.Instance.addAction(new TMoveAction(touchStartMoveObjects[t.Touch.fingerId],new GPObject(tangible)));
					}
					touchStartMoveTimes[t.Touch.fingerId] = 0;
					touchStartMoveObjects[t.Touch.fingerId] = null;
				}
			}
		}
		
		
		
		
		
		float angle = Mathf.Round((float)t.Properties.Angle);
		
		
		
		float normalizedNewAngle = Mathf.Abs((angle-45)/45);	
		float normalizedGameObjectAngle=Mathf.Abs((tangible.transform.eulerAngles.z-45)/45);	
		
		
		if(touchRotations[t.Touch.fingerId].Count > 10){
			if(Mathf.Abs(normalizedGameObjectAngle - (float)(touchRotationsNorm[t.Touch.fingerId].Average())) > 0.2f){
				if(touchRotationsNorm[t.Touch.fingerId].Average() > 0.88){
					tangible.transform.eulerAngles = new Vector3(0,0,0);
				}else if(touchRotations[t.Touch.fingerId].Average() >= 11 && touchRotations[t.Touch.fingerId].Average() < 34){
					tangible.transform.eulerAngles = new Vector3(0,0,22.5f);
				}else if(touchRotations[t.Touch.fingerId].Average() >= 34 && touchRotations[t.Touch.fingerId].Average() <= 57){
					tangible.transform.eulerAngles = new Vector3(0,0,45);
				}else if(touchRotations[t.Touch.fingerId].Average() > 57 && touchRotations[t.Touch.fingerId].Average() <= 89){
					tangible.transform.eulerAngles = new Vector3(0,0,67.5f);	
				}
			}
			touchRotations[t.Touch.fingerId].Clear();
			touchRotationsNorm[t.Touch.fingerId].Clear();
		}
		
		touchRotations[t.Touch.fingerId].Add(angle);
		touchRotationsNorm[t.Touch.fingerId].Add(normalizedNewAngle);
		
		
		//Debug.Log (angle);
		
		//angle simple rot
		/*
		if(Mathf.Abs(normalizedGameObjectAngle - normalizedNewAngle) > 0.1f){
			tangible.transform.rotation = Quaternion.Euler(new Vector3(0,0,angle));
		}
		*/
		
		//angles pas trop mal
		/*
		if(angle <= 3 || angle >= 87){
			tangible.transform.rotation = Quaternion.Euler(new Vector3(0,0,0));
		}else{
			if(Mathf.Abs(normalizedGameObjectAngle - normalizedNewAngle) > 0.2f){
				if(touchRotations[t.Touch.fingerId].Count > 100){
						angle = (float)touchRotations[t.Touch.fingerId].Average();
						
						touchRotations[t.Touch.fingerId].Clear();
					}
				tangible.transform.rotation = Quaternion.Euler(new Vector3(0,0,angle));
			}else{
				if(tangible.transform.eulerAngles.z < 3 || tangible.transform.eulerAngles.z > 87){
					tangible.transform.rotation = Quaternion.Euler(new Vector3(0,0,0));
				}else{
					if(touchRotations[t.Touch.fingerId].Count > 100){
						angle = (float)touchRotations[t.Touch.fingerId].Average();
						
						touchRotations[t.Touch.fingerId].Clear();
					}
					
					if(angle > 45 && tangible.transform.eulerAngles.z == 0){
						tangible.transform.eulerAngles = new Vector3(0,0,90);
					}
					
					normalizedNewAngle = Mathf.Abs((angle-45)/45);
					normalizedGameObjectAngle=Mathf.Abs((tangible.transform.eulerAngles.z-45)/45);	
					
					if(Mathf.Abs(normalizedGameObjectAngle - normalizedNewAngle) > 0.1f){
						tangible.transform.rotation = Quaternion.Euler(new Vector3(0,0,Mathf.Lerp(tangible.transform.eulerAngles.z,angle,Time.deltaTime*4)));
					}
					
					touchRotations[t.Touch.fingerId].Add(angle);
				}
			}
		}
		*/
				
	}
	
}
